#include <sys/_stdint.h>
#include "lcd_fun.h"
#include <Arduino.h>
#include <WiFiManager.h>  // https://github.com/tzapu/WiFiManager
#include <nvs_flash.h>
#include <LiquidCrystal_I2C.h>
#include "common_util.h"

// 这一堆活宝数据其实用数组+结构体存更合适。但是我不想。
// 十六个通道LCD的8574A(T)驱动芯片的I2C地址
char* lcdak[16] = { "la_0_key", "la_1_key", "la_2_key", "la_3_key", "la_4_key", "la_5_key", "la_6_key", "la_7_key",
                    "la_8_key", "la_9_key", "la_a_key", "la_b_key", "la_c_key", "la_d_key", "la_e_key", "la_f_key" };
uint8_t lcda[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// 十六个通道LCD的列
char* lcdck[16] = { "lc_0_key", "lc_1_key", "lc_2_key", "lc_3_key", "lc_4_key", "lc_5_key", "lc_6_key", "lc_7_key",
                    "lc_8_key", "lc_9_key", "lc_a_key", "lc_b_key", "lc_c_key", "lc_d_key", "lc_e_key", "lc_f_key" };
uint8_t lcdc[16] = { 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 };
// 十六个通道LCD的行
char* lcdrk[16] = { "lr_0_key", "lr_1_key", "lr_2_key", "lr_3_key", "lr_4_key", "lr_5_key", "lr_6_key", "lr_7_key",
                    "lr_8_key", "lr_9_key", "lr_a_key", "lr_b_key", "lr_c_key", "lr_d_key", "lr_e_key", "lr_f_key" };
uint8_t lcdr[16] = { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 };
// 十六个通道LCD的8574A(T)驱动芯片的背光开关（这个功能不建议使用因为8574A(T)单独控制背光经常抽风）
char* lcdbk[16] = { "lbl_0_key", "lbl_1_key", "lbl_2_key", "lbl_3_key", "lbl_4_key", "lbl_5_key", "lbl_6_key", "lbl_7_key",
                    "lbl_8_key", "lbl_9_key", "lbl_a_key", "lbl_b_key", "lbl_c_key", "lbl_d_key", "lbl_e_key", "lbl_f_key" };
uint8_t lcdb[16] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };

LiquidCrystal_I2C* lcds = (LiquidCrystal_I2C*)malloc(16 * sizeof(LiquidCrystal_I2C));

// 屏幕数据缓存
char lcds_data_cache[16][4][21];
// 缓存到屏幕数据同步状态。记录那些屏幕那些组需要缓存。每一行的每4个字符为一组。
uint8_t lcds_data_cache_syn[16][4][5];
// 总开关用来标记渲染线程是否退出
uint8_t lcds_data_cache_syn_run = 0;
TaskHandle_t lcdDataSynHandle = NULL;

// 滚屏功能缓存
char lcds_run_cache[16][21];
// 滚屏功能开始屏幕
uint8_t lcd_run_start = 8;

// 背光开关按键下标。确定是哪个按键可以开关背光。
uint8_t lcdbgKeyIndex = 0;
char* lcdbgKeyIndex_key = "lbki_key";

// 开机后，背光开关状态
uint8_t lcdbgDef = 1;
char* lcdbgDef_key = "l_b_d_key";

// 滚屏同步
uint8_t lcd_run_syn;

// GIO控制的背光闪烁繁忙标志位
uint8_t lcd_bf = 0;
// GIO控制的闪烁完毕后亮还是灭
uint8_t lcd_bb = 0;
TaskHandle_t taskLcdbfHandle = NULL;

// 屏幕下标0-15，列（从0开始），行（从0开始），数字
void lcdPrint(uint8_t i, uint8_t column, uint8_t row, unsigned long number) {
  if (lcda[i] == 0) {
    return;
  }
  String str = "";
  str.concat(number);
  lcdPrint(i, column, row, str);
}


// 屏幕下标0-15，列（从0开始），行（从0开始），字符串
void lcdPrint(uint8_t i, uint8_t column, uint8_t row, const char str[]) {
  if (lcda[i] == 0) {
    return;
  }
  for (uint8_t si = 0; column < lcdc[i] && str[si] > 0; column++, si++) {
    if (lcds_data_cache[i][row][column] != str[si] && str[si] >= 32) {
      lcds_data_cache[i][row][column] = str[si];
      lcds_data_cache_syn[i][row][column >> 2] = 1;
    }
  }
  doLcdDataSyn();
}

// 屏幕下标0-15，列（从0开始），行（从0开始），字符串
void lcdPrint(uint8_t i, uint8_t column, uint8_t row, char str[]) {
  if (lcda[i] == 0) {
    return;
  }
  for (uint8_t si = 0; column < lcdc[i] && str[si] > 0; column++, si++) {
    if (lcds_data_cache[i][row][column] != str[si] && str[si] >= 32) {
      lcds_data_cache[i][row][column] = str[si];
      lcds_data_cache_syn[i][row][column >> 2] = 1;
    }
  }
}

// 屏幕下标0-15，列（从0开始），行（从0开始），数字
char c;
void lcdPrint(uint8_t i, uint8_t column, uint8_t row, String str) {
  if (lcda[i] == 0) {
    return;
  }
  for (uint8_t si = 0; column < lcdc[i] && si < str.length(); column++, si++) {
    c = str.charAt(si);
    if (lcds_data_cache[i][row][column] != c && c >= 32) {
      lcds_data_cache[i][row][column] = c;
      lcds_data_cache_syn[i][row][column >> 2] = 1;
    }
  }
  doLcdDataSyn();
}

void LCDInit() {
  for (uint8_t i = 0; i < 16; i++) {
    // 读取保存的LCD屏幕配置信息
    // I2C地址
    nvs_get_u8(my_nvs_handle_t, lcdak[i], &lcda[i]);
    // 列
    nvs_get_u8(my_nvs_handle_t, lcdck[i], &lcdc[i]);
    // 行
    nvs_get_u8(my_nvs_handle_t, lcdrk[i], &lcdr[i]);
    //8574A(T)驱动芯片高电平开灯
    nvs_get_u8(my_nvs_handle_t, lcdbk[i], &lcdb[i]);

    nvs_get_u8(my_nvs_handle_t, lcdbgKeyIndex_key, &lcdbgKeyIndex);
    nvs_get_u8(my_nvs_handle_t, lcdbgGio_key, &lcdbgGio);
    nvs_get_u8(my_nvs_handle_t, lcdbgDef_key, &lcdbgDef);

    if (lcda[i] == 0) {
      continue;
    }
    lcds[i] = LiquidCrystal_I2C(lcda[i], lcdc[i], lcdr[i]);
    lcds[i].init();
    for (uint8_t j = 0; j < lcdr[i]; j++) {
      for (uint8_t k = 0; k < lcdc[i]; k++) {
        lcds_data_cache[i][j][k] = ' ';
      }
      for (uint8_t k = 0; k < 5; k++) {
        lcds_data_cache_syn[i][j][k] = 0;
      }
      lcds_data_cache[i][j][lcdc[i]] = '\0';
    }
    //8574A(T)驱动芯片高电平开灯
    if (lcdb[i]) {
      lcds[i].backlight();
    }
  }

  for (uint8_t i = 0; i < 16; i++) {
    for (uint8_t j = 0; j < 20; j++) {
      lcds_run_cache[i][j] = ' ';
    }
    lcds_run_cache[i][20] = '\0';
  }

  xTaskCreate(lcdDataSyn, "lcdDataSyn", 1024 * 10, NULL, 1, &lcdDataSynHandle);
  // 初始化GIO控制的背光灯
  pinMode(lcdbgGio, OUTPUT);
  if (lcdbgDef) {
    // 开灯
    digitalWrite(lcdbgGio, HIGH);
  } else {
    digitalWrite(lcdbgGio, LOW);
  }
  for (uint8_t i = 0; i < 16; i++) {
    if (lcda[i] == 0) {
      continue;
    }
    LiquidCrystal_I2C lcd = lcds[i];
    lcds_data_cache[i][0][7] = '#';
    lcds_data_cache[i][0][8] = (i / 10) + '0';
    lcds_data_cache[i][0][9] = (i % 10) + '0';

    lcds_data_cache[i][0][11] = (lcdc[i] / 10) + '0';
    lcds_data_cache[i][0][12] = (lcdc[i] % 10) + '0';

    lcds_data_cache[i][0][13] = '*';
    lcds_data_cache[i][0][14] = (lcdr[i] / 10) + '0';
    lcds_data_cache[i][0][15] = (lcdr[i] % 10) + '0';

    lcds_data_cache_syn[i][0][1] = 1;
    lcds_data_cache_syn[i][0][2] = 1;
    lcds_data_cache_syn[i][0][3] = 1;
  }
  // 这里先不更新了，初始化显示的东西还多，等别的的地方统一更新
  // doLcdDataSyn();
  for (uint8_t i = 0; i < 16; i++) {
    Serial.print("lcd_");
    Serial.print(t2h(i));
    Serial.print(" addr:");
    Serial.print(lcda[i]);
    Serial.print("   lcd_");
    Serial.print(t2h(i));
    Serial.print("a63   lcd_");
    Serial.print(t2h(i));
    Serial.println("a39");
    if (lcda[i] == 0) {
      continue;
    }
    Serial.print("lcd_");
    Serial.print(t2h(i));
    Serial.print(" cols:");
    Serial.print(lcdc[i]);
    Serial.print("   lcd_");
    Serial.print(t2h(i));
    Serial.print("c20   lcd_");
    Serial.print(t2h(i));
    Serial.println("c16");
    Serial.print("lcd_");
    Serial.print(t2h(i));
    Serial.print(" rows:");
    Serial.print(lcdr[i]);
    Serial.print("   lcd_");
    Serial.print(t2h(i));
    Serial.print("r4   lcd_");
    Serial.print(t2h(i));
    Serial.println("r2");
    Serial.print("lcd_");
    Serial.print(t2h(i));
    Serial.print(" dl:");
    Serial.print(lcdb[i]);
    Serial.print("   lcd_");
    Serial.print(t2h(i));
    Serial.print("d0   lcd_");
    Serial.print(t2h(i));
    Serial.println("d1");
  }

  Serial.print("LCD backlight pin:");
  Serial.print(lcdbgGio);
  Serial.println("   LCD_P01   LCD_P02");
  Serial.print("LCD backlight def:");
  Serial.print(lcdbgDef);
  Serial.println("   LCD_H1   LCD_H0");
  Serial.print("LCD lcdbgKeyIndex def:");
  Serial.println(lcdbgKeyIndex);
  Serial.println("only:SYS_B0   SYS_B1   SYS_B2   SYS_B3   SYS_B4");
  Serial.println("disable:SYS_B5");

  xTaskCreate(taskLcdbf, "taskLcdbf", 512, NULL, 1, &taskLcdbfHandle);
}

uint8_t requestLCD(String request, int len) {
  if (!(request.startsWith("LCD_") || request.startsWith("lcd_"))) {
    return 0;
  }
  if (len < 5) {
    return 1;
  }
  uint8_t index = request.charAt(4);
  if (index == 'R' || index == 'r') {
    lcdRun(request.substring(5, 25));
    return 1;
  }
  if (len < 6) {
    return 1;
  }
  uint8_t c5 = request.charAt(5);
  if ((index == 'G' || index == 'g') && lcdbgGio < 99) {
    if (c5 == '1') {
      digitalWrite(lcdbgGio, HIGH);
    } else if (c5 == '0') {
      digitalWrite(lcdbgGio, LOW);
    } else {
      if (digitalRead(lcdbgGio)) {
        broadcast("LCD_G1");
      } else {
        broadcast("LCD_G0");
      }
    }
    return 1;
  }
  if (index == 'H' || index == 'h') {
    Serial.print("Set backlight def:");
    if (c5 == '1') {
      if (lcdbgGio < 99) {
        digitalWrite(lcdbgGio, HIGH);
      }
      saveVlaueU8(lcdbgDef_key, 1);
      Serial.println("on");
    } else {
      if (lcdbgGio < 99) {
        digitalWrite(lcdbgGio, LOW);
      }
      saveVlaueU8(lcdbgDef_key, 0);
      Serial.println("off");
    }
    return 1;
  }

  if ((index == 'I' || index == 'i') && lcdbgGio < 99) {
    lcd_bf = 1;
    if (c5 == '1') {
      lcd_bb = 1;
    } else if (c5 == '0') {
      lcd_bb = 0;
    } else {
      lcd_bb = digitalRead(lcdbgGio);
    }
    vTaskResume(taskLcdbfHandle);
    return 1;
  }

  if ((index == 'P' || index == 'p') && len >= 7) {
    Serial.print("Set backlight pin:");
    uint8_t pin = (c5 - '0') * 10 + (request.charAt(6) - '0');
    saveVlaueU8(lcdbgGio_key, pin);
    Serial.println(pin);
    return 1;
  }

  if (index == 'B' || index == 'b') {
    uint8_t key = c5 - '0';
    Serial.print("Set bg key index:");
    saveVlaueU8(lcdbgKeyIndex_key, key);
    Serial.println(key);
    return 1;
  }

  index = s2h(index);
  if (index < 0 || index > 15) {
    return 1;
  }
  if ((c5 == 'A' || c5 == 'a') && len >= 8) {
    uint8_t addr = (request.charAt(6) - '0') * 10 + (request.charAt(7) - '0');
    Serial.print("new lcd addr:");
    Serial.println(addr);
    saveVlaueU8(lcdak[index], addr);
    return 1;
  }
  if ((c5 == 'C' || c5 == 'c') && len >= 8) {
    uint8_t column = (request.charAt(6) - '0') * 10 + (request.charAt(7) - '0');
    Serial.print("new lcd column:");
    Serial.println(column);
    saveVlaueU8(lcdck[index], column);
    return 1;
  }
  if ((c5 == 'R' || c5 == 'r') && len >= 7) {
    uint8_t rows = request.charAt(6) - '0';
    Serial.print("new lcd rows:");
    Serial.println(rows);
    saveVlaueU8(lcdrk[index], rows);
    return 1;
  }
  if ((c5 == 'D' || c5 == 'd') && len >= 7) {
    Serial.print("Set default backlight:");
    if (request.charAt(6) == '1') {
      if (lcda[index] > 0) {
        lcds[index].backlight();
      }
      saveVlaueU8(lcdbk[index], 1);
      Serial.println("on");
    } else {
      if (lcda[index] > 0) {
        lcds[index].noBacklight();
      }
      saveVlaueU8(lcdbk[index], 0);
      Serial.println("off");
    }
    return 1;
  }
  if (lcda[index] == 0) {
    return 1;
  }
  LiquidCrystal_I2C lcd = lcds[index];
  if ((c5 == 'B' || c5 == 'b') && len >= 7) {
    if (request.charAt(6) == '1') {
      lcd.backlight();
    } else {
      lcd.noBacklight();
    }
    return 1;
  }
  if (len == 5) {
    for (uint8_t j = 0; j < lcdr[index]; j++) {
      for (uint8_t k = 0; k < lcdc[index]; k++) {
        lcds_data_cache[index][j][k] = ' ';
      }
    }
    lcd.clear();
    return 1;
  }
  if (len > 8) {
    lcdPrint(index, (c5 - '0') * 10 + (request.charAt(6) - '0'), request.charAt(7) - '0', request.substring(8, len));
    return 1;
  }
  return 1;
}

void lcdRun(String request) {
  String lcdRunEnd = "LCD_R";
  lcdRunEnd.concat(lcds_run_cache[15]);
  for (uint8_t i = 15; i > 0; i--) {
    for (uint8_t j = 0; j < 20; j++) {
      lcds_run_cache[i][j] = lcds_run_cache[i - 1][j];
    }
  }
  uint8_t j = 0;
  for (; j < 20 && j < request.length(); j++) {
    lcds_run_cache[0][j] = request.charAt(j);
  }
  for (; j < 20; j++) {
    lcds_run_cache[0][j] = ' ';
  }
  uint8_t lrci = 0;
  for (uint8_t i = lcd_run_start; i < 16; i++) {
    if (lcda[i] == 0) {
      continue;
    }
    for (uint8_t j = 0; j < lcdr[i] && lrci < 16; j++, lrci++) {
      for (uint8_t k = 0; k < lcdc[i] && k < 20; k++) {
        if (lcds_data_cache[i][j][k] != lcds_run_cache[lrci][k]) {
          lcds_data_cache[i][j][k] = lcds_run_cache[lrci][k];
          lcds_data_cache_syn[i][j][k >> 2] = 1;
        }
      }
    }
  }
  if (lrci != 15) {
    lcdRunEnd.clear();
    lcdRunEnd.concat("LCD_R");
    lcdRunEnd.concat(lcds_run_cache[lrci]);
  }
  broadcast(lcdRunEnd);
  doLcdDataSyn();
}

void doLcdDataSyn() {
  lcds_data_cache_syn_run = 1;
  vTaskResume(lcdDataSynHandle);
}

void lcdDataSyn(void* ptParam) {
  uint8_t i, j, k, l, m, ns;
  while (true) {
    if (lcds_data_cache_syn_run) {
      lcds_data_cache_syn_run = 0;
      for (i = 0; i < 16; i++) {
        if (lcda[i] == 0) {
          continue;
        }
        LiquidCrystal_I2C lcd = lcds[i];
        for (j = 0; j < lcdr[i]; j++) {
          for (k = 0; k < 5; k++) {
            if (lcds_data_cache_syn[i][j][k]) {
              lcds_data_cache_syn[i][j][k] = 0;
              l = k << 2;
              // 为了少调I2C更新LCD的指令，我也是够够的了
              if (ns) {
                lcd.setCursor(l, j);
              }
              for (m = 0; m < 4; m++, l++) {
                // 主要就是这里慢，弄这么多变量缓存就是为了少调它
                lcd.print(lcds_data_cache[i][j][l]);
              }
              // 如果同行的下一组也要刷新
              if (k < 4 && lcds_data_cache_syn[i][j][k + 1]) {
                // 那就刚好告诉下一组，不用执行光标移动操作了。
                ns = 0;
              } else {
                // 否则下一次需要更新光标。
                ns = 1;
              }
            }
          }
        }
      }
    } else {
      vTaskSuspend(NULL);
    }
  }
}

void taskLcdbf(void* ptParam) {
  while (true) {
    if (lcd_bf) {
      // 背光闪烁繁忙标志位清零
      lcd_bf = 0;
      for (uint8_t i = 0; i < 3; i++) {
        digitalWrite(lcdbgGio, HIGH);
        delay(1000);
        digitalWrite(lcdbgGio, LOW);
        delay(1000);
      }
      if (lcd_bb > 0) {
        digitalWrite(lcdbgGio, HIGH);
      }
    } else {
      vTaskSuspend(NULL);
    }
  }
}
